// 1. 读取入口文件
const fs = require('fs');
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default
const path = require('path')
const babelCore = require('@babel/core')

const getModuleInfo = (file) => {
    const body = fs.readFileSync(file, 'utf-8')
    // 2. 分析入口文件的代码，读取入口文件所有依赖的模块， 生成 AST 语法树（用对象来描述代码）
    const AST = parser.parse(body, {
        sourceType: 'module', // 告诉 babel 我们的代码是 ES 模块
        // plugins: ['dynamicImport'], // 开启动态导入的插件
    })
    // 3. 遍历AST(抽象语法树)，收集依赖
    const deps = {}
    traverse(AST, {
        ImportDeclaration({ node }) {
            // console.log(path.node.source.value);
            const dirname = path.dirname(file)
            const absolutePath = './' + path.join(dirname, node.source.value);
            // console.log(absolutePath);
            deps[node.source.value] = absolutePath
        }
    })
    // 4. 根据 AST 生成低版本代码
    const { code } = babelCore.transformFromAst(AST, null, {
        presets: ['@babel/preset-env'],
    })
    // console.log(AST.program.body);
    // console.log(deps);
    // console.log(code);
    const moduleInfo = {
        file, // 入口文件
        deps, // 依赖的模块
        code, // 低版本代码
    }
    return moduleInfo
}
const parseModules = (file) => {
    const entry = getModuleInfo(file)
    // 递归读取依赖
    const temp = [entry];
    for (let i = 0; i < temp.length; i++) {
        const deps = temp[i].deps // file.deps --> moduleInfo{file,deps,code}
        if (deps) {
            for (let key in deps) {
                if (deps.hasOwnProperty(key)) {  // 避免原型链上的属性
                    temp.push(getModuleInfo(deps[key]))
                }
            }
        }
    }
    // 5. 生成依赖图谱
    const graph = {}
    temp.forEach(item => {
        graph[item.file] = {
            deps: item.deps,
            code: item.code,
        }
    })

    return graph

}
// 处理 require函数和exports对象
const bundle = (file) => {
    const depsGraph = JSON.stringify(parseModules(file));
    return `(function(graph) {
            function require(file) {
              function absRequire(relPath) {
                return require(graph[file].deps[relPath])
              }
              var exports = {};
              (function(require, exports, code) {
                eval(code)
              })(absRequire, exports, graph[file].code);
              return exports
            }
            require('${file}')
          })(${depsGraph});`
}

const code = bundle('./src/index.js')

fs.mkdirSync('./dist')
fs.writeFileSync('./dist/bundle.js', code)

// console.log(code);



